package com.bestcem.xm.user.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.ObjectId;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.bestcem.xm.common.core.constant.ResourceErrorCodeConstants;
import com.bestcem.xm.common.core.domain.web.ServiceResult;
import com.bestcem.xm.common.core.enums.ReqErrorCodeEnum;
import com.bestcem.xm.common.core.enums.ResourceErrorCodeEnum;
import com.bestcem.xm.common.core.exception.BusinessException;
import com.bestcem.xm.component.security.context.SecurityContextHolder;
import com.bestcem.xm.om.dao.mapper.UserOrganizationMapper;
import com.bestcem.xm.user.constant.UserGroupConstant;
import com.bestcem.xm.user.dao.UserGroupDao;
import com.bestcem.xm.user.dao.UserUserGroupDao;
import com.bestcem.xm.user.dao.mapper.UserGroupLevelMapper;
import com.bestcem.xm.user.dao.mapper.UserGroupMapper;
import com.bestcem.xm.user.dao.mapper.UserUserGroupMapper;
import com.bestcem.xm.user.entity.mysql.UserGroup;
import com.bestcem.xm.user.entity.mysql.UserGroupLevel;
import com.bestcem.xm.user.entity.mysql.UserUserGroup;
import com.bestcem.xm.user.entity.pojo.UserUserGroupDO;
import com.bestcem.xm.user.enums.HaveChildrenFlagEnum;
import com.bestcem.xm.user.enums.UserGroupOperateUserEnum;
import com.bestcem.xm.user.enums.UserGroupStatusEnum;
import com.bestcem.xm.user.enums.UserStatusEnum;
import com.bestcem.xm.user.grpc.service.param.GroupIdsPageParam;
import com.bestcem.xm.user.mq.info.group.GroupNameInfo;
import com.bestcem.xm.user.mq.info.group.StoreInfo;
import com.bestcem.xm.user.mq.message.group.ChangeGroupCodeMessage;
import com.bestcem.xm.user.mq.send.biz.UserMessageSendService;
import com.bestcem.xm.user.service.SequenceService;
import com.bestcem.xm.user.service.UserGroupService;
import com.bestcem.xm.user.service.UserUserGroupService;
import com.bestcem.xm.user.service.dto.PageResponseDTO;
import com.bestcem.xm.user.service.dto.group.*;
import com.bestcem.xm.user.util.business.GroupSequenceUtil;
import com.bestcem.xm.user.util.convert.UserGroupConvert;
import com.bestcem.xm.user.util.convert.UserUserGroupConvert;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.bestcem.xm.common.core.enums.ReqErrorCodeEnum.PARAM_FORMAT;
import static com.bestcem.xm.common.core.enums.ResourceErrorCodeEnum.FORBIDDEN;
import static com.bestcem.xm.common.core.enums.ResourceErrorCodeEnum.STATUS_CHANGED;
import static com.bestcem.xm.user.enums.GroupStatusEnum.*;


/**
 * 用户组关联组织
 *
 * @author Linked <zhijian.lin@idiaoyan.com>
 * @version v1.0
 * @date 2021/12/29 16:02
 */

@Service
@Slf4j
public class UserUserGroupServiceImpl implements UserUserGroupService {
    @Resource
    private UserUserGroupDao userUserGroupDao;
    @Resource
    private UserUserGroupConvert userUserGroupConvert;
    @Resource
    private UserGroupService userGroupService;
    @Autowired
    private UserUserGroupMapper userUserGroupMapper;
    @Autowired
    private UserGroupDao userGroupDao;
    @Resource
    private GroupSequenceUtil groupSequenceUtil;
    @Resource
    private UserGroupMapper userGroupMapper;
    @Resource
    private UserGroupLevelMapper userGroupLevelMapper;
    @Resource
    private SequenceService sequenceService;
    @Resource
    private UserMessageSendService userMessageSendService;

    @Resource
    private UserGroupConvert userGroupConvert;

    @Resource
    UserOrganizationMapper userOrganizationMapper;

    /**
     * 保存用户关联组织
     *
     * @param userToGroupDTO:
     * @return 主键id
     * @author Linked <zhijian.lin@idiaoyan.com>
     * @date 2022/1/6 17:32
     */
    @Override
    public ServiceResult<String> saveUserToGroup(SaveUserToGroupDTO userToGroupDTO) {
        UserUserGroupDO userUserGroupDO = userUserGroupConvert.userToGroupDTOToDO(userToGroupDTO);
        userUserGroupDO.setCreatorId(userToGroupDTO.getCreatorId());
        userUserGroupDO.setOperatorId(userToGroupDTO.getOperatorId());
        return ServiceResult.success(userUserGroupDao.saveUserToGroup(userUserGroupDO));
    }

    @Override
    public ServiceResult<Integer> insertUserGroupList(List<UserUserGroupDTO> userGroups) {
        List<UserUserGroupDO> dos = userUserGroupConvert.dtosToDos(userGroups);
        return ServiceResult.success(userUserGroupDao.insertUserGroupList(dos));
    }

    /**
     * 将账号移除当前组织
     *
     * @param param 参数信息
     * @return com.bestcem.xm.util.ServiceResult<java.lang.Integer>
     * @author Linzhijian <zhijian.lin@idiaoyan.com>
     * @date 2022/04/02 17:27
     */
    @Override
    public ServiceResult<Integer> deleteUserFromGroup(DelUserFromGroupDTO param) {
        // 根据id删除用戶关联组
        UserUserGroupDO groupDo = new UserUserGroupDO();
        groupDo.setUserGroupId(param.getUserGroupId());
        Integer deleteResult = userUserGroupDao.deleteBySelective(groupDo);
        return ServiceResult.success(deleteResult);
    }


    /**
     * 查询当前组织列表下的所有账号
     *
     * @param param
     * @return com.bestcem.xm.util.ServiceResult<java.util.List < java.lang.String>>
     * @author panyun <yun.pan@idiaoyan.com>
     * @date 2022/1/25 15:08
     */
    @Override
    public ServiceResult<List<String>> listUserFromGroups(GroupIdsPageParam param) {
        // 获取当前组织列表下的用户id并去重
        List<String> userIds = userUserGroupDao.getUserIdsByGroupIds(param);
        List<String> collect = userIds.stream().distinct().collect(Collectors.toList());
        return ServiceResult.success(collect);
    }

    @Override
    public ServiceResult<Integer> getUserCountByGroupIds(List<String> groupIds) {
        return ServiceResult.success(userUserGroupDao.getUserCountByGroupIds(groupIds));
    }

    /**
     * 批量操作用户组的用户列表信息
     *
     * @param groupId       用户组id
     * @param rootGroupId   根组织id
     * @param operate       操作类型
     * @param userIds       用户id列表
     * @param operateUserId 操作人ID
     * @return com.bestcem.xm.util.ServiceResult<java.lang.Integer>
     * @author cuiqiongyu <qiongyu.cui@idiaoyan.cn>
     * @date 2021/12/30 16:14
     */
    @Override
    public ServiceResult<Integer> batchOperateUser(String groupId, String rootGroupId, UserGroupOperateUserEnum operate, List<String> userIds, String operateUserId) {
        switch (operate) {
            case ADD:
                // 查询出已存在的关联信息
                List<String> existUserIds = userUserGroupDao.listUserIds(groupId, userIds);
                userIds.removeAll(existUserIds);
                userIds.stream().forEach(userId -> {
                    UserUserGroupDO userUserGroupDO = new UserUserGroupDO();
                    userUserGroupDO.setGroupId(groupId);
                    userUserGroupDO.setUserId(userId);
                    userUserGroupDO.setRootGroupId(rootGroupId);
                    userUserGroupDO.setCreatorId(operateUserId);
                    userUserGroupDO.setOperatorId(operateUserId);
                    userUserGroupDao.saveUserToGroup(userUserGroupDO);
                });
                return ServiceResult.success(userIds.size());
            case REMOVE:
                Integer deleteNum = userUserGroupDao.deleteByGroupIdAndUserIds(groupId, userIds);
                return ServiceResult.success(deleteNum);
            default:
                break;
        }
        return ServiceResult.fail(PARAM_FORMAT, "无效的操作类型");
    }


    @Override
    public ServiceResult<List<UserUserGroupDTO>> listByGroupIdAndUserId(String rootGroupId, String groupId, String userId) {
        // 校验参数
        if (!ObjectId.isValid(rootGroupId)) {
            return ServiceResult.fail("根组织id格式不正确");
        }
        if (!ObjectId.isValid(groupId)) {
            return ServiceResult.fail("组织id格式不正确");
        }
        if (!ObjectId.isValid(userId)) {
            return ServiceResult.fail("用户id格式不正确");
        }

        // 设置查询参数
        UserUserGroupDO param = new UserUserGroupDO();
        param.setRootGroupId(rootGroupId);
        param.setGroupId(groupId);
        param.setUserId(userId);

        // 查询
        List<UserUserGroupDO> results = userUserGroupDao.listByCondition(param);
        // 设置返回
        return ServiceResult.success(userUserGroupConvert.doToDto(results));
    }

    /**
     * 更新用户关联组列表信息
     *
     * @param orgId        租户id
     * @param userId       用户id
     * @param groupIdsList 组织id列表
     * @return com.bestcem.xm.util.ServiceResult<java.util.List < java.lang.String>>
     * @author cuiqiongyu <qiongyu.cui@idiaoyan.cn>
     * @date 2022/01/24 11:52
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ServiceResult<List<String>> updateUserGroups(String orgId, String userId, List<String> groupIdsList) {
        // 参数校验
        if (ObjectUtil.isEmpty(orgId)) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "租户id为空或者格式不对");
        }
        if (ObjectUtil.isEmpty(userId)) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "userId为空或者格式不对");
        }
        if (CollectionUtils.isEmpty(groupIdsList)) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "组织id列表为空或者组织id列表格式不对");
        }

        // 参数校验，校验组织id的有效性
        ServiceResult<List<UserGroupDTO>> groupsByOrgIdAndGroupIds = userGroupService.getGroupsByOrgIdAndGroupIds(orgId, groupIdsList);
        // 获取传入的组织id去重的数量
        Set<String> groupIdSet = groupIdsList.stream().map(g -> g.toLowerCase(Locale.ROOT)).collect(Collectors.toSet());
        int groupIdSize = groupIdSet.size();
        if (!groupsByOrgIdAndGroupIds.isSuccess() || !Integer.valueOf(groupIdSize).equals(groupsByOrgIdAndGroupIds.getData().size())) {
            return ServiceResult.fail(ResourceErrorCodeEnum.NOT_FOUND, "组织id列表中存在无效的组织id");
        }

        // 查询当前租户下跟组织id
        ServiceResult<UserGroupDTO> rootGroupByOrgId = userGroupService.getRootGroupByOrgId(orgId);
        if (!rootGroupByOrgId.isSuccess() || Objects.isNull(rootGroupByOrgId.getData())) {
            return ServiceResult.fail(ResourceErrorCodeEnum.NOT_FOUND, "未查询到当前租户下根组织信息");
        }
        String rootGroupId = rootGroupByOrgId.getData().getGroupId();

        // 删除原有的数据
        UserUserGroupDO deleteParam = new UserUserGroupDO();
        deleteParam.setRootGroupId(rootGroupId);
        deleteParam.setUserId(userId);
        userUserGroupDao.deleteBySelective(deleteParam);

        // 新增新数据
        List<String> result = new ArrayList<>();
        for (String groupId : groupIdSet) {
            UserUserGroupDO userUserGroupDO = new UserUserGroupDO();
            userUserGroupDO.setGroupId(groupId);
            userUserGroupDO.setRootGroupId(rootGroupId);
            userUserGroupDO.setUserId(userId);
            userUserGroupDO.setCreatorId(null);
            userUserGroupDO.setOperatorId(null);
            userUserGroupDao.saveUserToGroup(userUserGroupDO);
            result.add(groupId);
        }
        return ServiceResult.success(result);
    }


    /**
     * 根据rootGroupID、UserID、groupID获取用户组关联信息列表
     *
     * @param userUserGroupDTO 前端传入的usergroup
     * @param page             页数
     * @param size             分页大小
     * @return com.bestcem.xm.util.ServiceResult<PageResponseDTO < UserUserGroupDTO>>
     * @author yanjishuo <jishuo.yan@idiaoyan.cn>
     * @date 2022/01/25 14:00
     */
    @Override
    public ServiceResult<PageResponseDTO<UserUserGroupDTO>> getUserUserGroupList(UserUserGroupDTO userUserGroupDTO, Integer page, Integer size) {
        // 参数校验
        if (Objects.isNull(page)) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "页数为空或者格式不对");
        }
        if (Objects.isNull(size)) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "分页大小为空或者格式不对");
        }
        if (Objects.isNull(userUserGroupDTO)) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "userUserGroupDTO为空或者格式不对");
        }
        PageResponseDTO<UserUserGroupDTO> result = new PageResponseDTO<>();
        result.setPage(page);

        UserUserGroupDO userUserGroupDO = userUserGroupConvert.dtoToDo(userUserGroupDTO);
        Long num = userUserGroupDao.count(userUserGroupDO);
        if (num == 0L) {
            result.setRows(new ArrayList<>());
            result.setTotal(0L);
            return ServiceResult.success(result);
        }

        List<UserUserGroupDO> userUserGroupDOS = userUserGroupDao.getUserUserGroupByEachId(userUserGroupDO, page, size);
        result.setRows(userUserGroupConvert.doToDto(userUserGroupDOS));
        result.setTotal(num);

        return ServiceResult.success(result);
    }

    /**
     * 根据userId查询关联的groupId列表
     *
     * @param userId
     * @return java.util.List<java.lang.String>
     * @author panyun <yun.pan@idiaoyan.com>
     * @date 2022/2/11 0:00
     */
    @Override
    public ServiceResult<List<String>> getGroupIdsByUserId(String userId) {
        List<String> groupIdsByUserId = userUserGroupDao.getGroupIdsByUserId(userId);
        return ServiceResult.success(groupIdsByUserId);
    }

    /**
     * 查询对应的用户数量
     *
     * @param groupIds 组织id列表
     * @return com.bestcem.xm.util.ServiceResult<java.util.Map < java.lang.String, java.lang.Integer>>
     * @author cuiqiongyu <qiongyu.cui@idiaoyan.cn>
     * @date 2022/02/08 14:08
     */
    @Override
    public ServiceResult<Map<String, Integer>> countUserNumByGroupIds(List<String> groupIds) {
        return ServiceResult.success(userUserGroupDao.countUserNumByGroupIds(groupIds));
    }

    @Override
    public long countUserNumByGroupIdAndLevelId(String groupId, String groupLevelId) {
        return userGroupDao.getSelfAndChildrenUserNum(UserStatusEnum.ACTIVATED.getStatus(), groupId, groupLevelId, null, UserGroupStatusEnum.ACTIVATED.getStatus());
    }

    @Override
    public ServiceResult<List<UserWithGroupDTO>> getUserWithGroup(String userId) {
        return ServiceResult.success(userUserGroupConvert.userWithGroupDO2Dto(userUserGroupDao.getUserWithGroup(userId)));
    }

    @Override
    public ServiceResult<List<String>> listByGroupId(String groupId) {
        UserUserGroupDO param = new UserUserGroupDO();
        param.setGroupId(groupId);
        List<UserUserGroupDO> userUserGroupDOS = userUserGroupDao.listByCondition(param);
        if (CollectionUtils.isEmpty(userUserGroupDOS)) {
            return ServiceResult.success(new ArrayList<>());
        }
        List<String> userIds = userUserGroupDOS.stream().map(UserUserGroupDO::getUserId).collect(Collectors.toList());
        return ServiceResult.success(userIds);
    }

    @Override
    public ServiceResult<List<String>> listGroupIdBySelective(UserUserGroupDTO userUserGroupDTO) {
        UserUserGroupDO param = userUserGroupConvert.dtoToDo(userUserGroupDTO);
        List<UserUserGroupDO> userUserGroupDOS = userUserGroupDao.listByCondition(param);
        if (CollectionUtils.isEmpty(userUserGroupDOS)) {
            return ServiceResult.success(new ArrayList<>());
        }
        List<String> userIds = userUserGroupDOS.stream().map(UserUserGroupDO::getGroupId).collect(Collectors.toList());
        return ServiceResult.success(userIds);
    }

    @Override
    public ServiceResult<Integer> deleteByGroupId(String groupId) {
        Integer deleteNum = userUserGroupDao.deleteByGroupIdAndUserIds(groupId, null);
        return ServiceResult.success(deleteNum);
    }

    @Override
    public ServiceResult<Void> putGroupTree(String orgId, JSONObject params) {
        String userId = SecurityContextHolder.getToken().getUserId();
        JSONObject tree = params.getJSONObject("tree");
        if (CollUtil.isEmpty(tree)) {
            throw new BusinessException(ReqErrorCodeEnum.PARAM_LOST.toString(), "tree is required");
        }
        List<JSONObject> levels = params.getJSONArray("groupLevelList").toJavaList(JSONObject.class);
        if (CollUtil.isEmpty(levels)) {
            throw new BusinessException(ReqErrorCodeEnum.PARAM_LOST.toString(), "groupLevelList is required");
        }
        Integer version = params.getInteger("version");
        if (Objects.isNull(version)) {
            throw new BusinessException(ReqErrorCodeEnum.PARAM_LOST.toString(), "version is required");
        }
        List<String> delete_grp_id_list = params.getJSONArray("deletedGroupIDList").toJavaList(String.class);
        //List delete_lv_id_list = params.getJSONArray("deletedGroupLevelIDList");
        List<JSONObject> del_ug_list = params.getJSONArray("del_ug").toJavaList(JSONObject.class);
        del_ug_list.forEach(ug -> userUserGroupMapper.deleteByUserIdAndGroupId(ug.getString("user_id"), ug.getString("group_id")));
        updateGroupTree(orgId, userId, tree, levels, delete_grp_id_list, version);
        return ServiceResult.success();
    }

    /**
     * 更新层级、层级树、用户组关系
     *
     * @param orgId           公司ID
     * @param userId
     * @param tree            层级树结构
     * @param levels          层级名称的数组
     * @param del_grp_id_list 删除的层级节点（用户组）ID数组
     * @param version         版本
     */
    private void updateGroupTree(String orgId, String userId, JSONObject tree, List<JSONObject> levels, List<String> del_grp_id_list, Integer version) {
        String root_id = tree.getString("id");
        int cur_ver = groupSequenceUtil.getVersion(root_id);
        if (version < cur_ver) {
            throw new BusinessException(String.valueOf(STATUS_CHANGED.getCode()), "group tree changed");
        }
        updateGroupTree(orgId, userId, tree, levels);
        if (CollUtil.isNotEmpty(del_grp_id_list)) {
            //还应该查询这些节点的子节点  目前靠前端传
            userGroupMapper.updateStatusByIds(del_grp_id_list, GROUP_STATUS_INACTIVATED.getIndex());
            userMessageSendService.publishGroupInvalidateList(orgId, del_grp_id_list);
        }
        incGroupVersionWithMq(root_id, orgId);
        //调整已删除节点
        incGroupVersion(root_id);
    }

    private void incGroupVersionWithMq(String root_id, String orgId) {
        userMessageSendService.publishGroupsChange(orgId, root_id);
        incGroupVersion(root_id);
    }

    /**
     * 增加某个层级结构的版本号
     *
     * @param root_id 层级结构根节点ID
     * @return 新版本号 整数
     */
    private void incGroupVersion(String root_id) {
        sequenceService.incrementVersion("group_ver_" + root_id);
    }

    /**
     * 更新层级结构
     *
     * @param orgId
     * @param tree
     * @param levels
     */
    private void updateGroupTree(String orgId, String userId, JSONObject tree, List<JSONObject> levels) {
        // 获取所有的group_id 和 title 名称，用来判断是否修改了组织名称
        // 获取所有的group_id 和 code，用来判断是否修改了组织code，进而修改二维码链接投放的链接和二维码
        String root_id = tree.getString("id");
        Map<String, String> old_group_id_title_dict = new HashMap();
        Map<String, String> old_group_id_code_dict = new HashMap();
        Map<String, Integer> old_group_id_status_dict = new HashMap();
        List<UserGroup> groups = userGroupMapper.listByOrgIdAndStatuses(orgId, null);
        groups.forEach(tmp -> {
            old_group_id_title_dict.put(tmp.getGroupId(), tmp.getGroupName());
            old_group_id_code_dict.put(tmp.getGroupId(), tmp.getGroupCode());
            old_group_id_status_dict.put(tmp.getGroupId(), tmp.getGroupStatus());
        });
        List<UserGroupLevel> old_levels = getGrouplevels(root_id);
        Map<String, UserGroupLevel> old_lv_dict = old_levels.stream().collect(Collectors.toMap(UserGroupLevel::getGroupLevelId, Function.identity()));
        List<String> lv_titles = levels.stream().map(v -> v.getString("title")).collect(Collectors.toList());
        lv_titles.forEach(title -> {
            if (title.length() > 31) {
                throw new BusinessException(String.valueOf(PARAM_FORMAT.getCode()), String.format("GroupLevel[title={%s}] is too long", title));
            }
        });

        old_levels = new ArrayList<>();
        Map<String, UserGroupLevel> new_titles = new HashMap<>();
        for (int lv_idx = 0; lv_idx < levels.size(); lv_idx++) {
            JSONObject lv = levels.get(lv_idx);
            String lv_id = lv.getString("id");
            UserGroupLevel lv_obj;
            if (StrUtil.isNotEmpty(lv_id) && old_lv_dict.containsKey(lv_id)) {
                lv_obj = old_lv_dict.remove(lv_id);
                old_levels.add(lv_obj);
            } else {
                lv_obj = new UserGroupLevel();
                lv_obj.setOrgId(orgId);
                lv_obj.setLevelNum(lv_idx);
                //lv_obj.setGroupLevelId(new org.bson.types.ObjectId().toHexString());
                old_levels.add(lv_obj);
            }
            String title = lv.getString("title");
            lv_obj.setGroupLevelName(title);
            lv_obj.setLevelNum(lv_idx);
            new_titles.put(title, lv_obj);
        }
        String nxt_id = null;
        Collections.reverse(old_levels);
        for (UserGroupLevel lv : old_levels) {
            lv.setLevelChildrenId(nxt_id);
            nxt_id = lv.getGroupLevelId();
        }
        if (CollUtil.isNotEmpty(old_lv_dict)) {
            userGroupMapper.deleteByIds(new ArrayList<>(old_lv_dict.keySet()));
        }
        old_levels.forEach(lv -> {
            lv.setOperatorId(userId);
            if (StrUtil.isEmpty(lv.getGroupLevelId())) {
                //新增
                lv.setCreatorId(userId);
                lv.setVersionId(UserGroupConstant.INITIAL_VERSION);
                lv.setGroupLevelId(new org.bson.types.ObjectId().toHexString());
                lv.setLevelDisplay(new JSONObject().toJSONString());
                userGroupLevelMapper.insertSelective(lv);
            } else {
                //更新
                lv.setVersionId(lv.getVersionId() + 1);
                userGroupLevelMapper.updateByPrimaryKeySelective(lv);
            }
        });
        levels = lv_titles.stream().map(v -> {
            UserGroupLevel lv = new_titles.get(v);
            JSONObject obj = new JSONObject();
            obj.put("id", lv.getGroupLevelId());
            obj.put("title", lv.getGroupLevelName());
            return obj;
        }).collect(Collectors.toList());

        int store_level = levels.size() - 1;
        //需要删除的节点id
        List<String> delete_list = new ArrayList();
        //[(group_id, old_title, new_title)]
        List<GroupNameInfo> title_modify = new ArrayList();
        //[(group_id, old_code, new_code)]
        List<ChangeGroupCodeMessage> groupcode_modify = new ArrayList();
        List<StoreInfo> newstore = new ArrayList();
        List<String> validate_list = new ArrayList();
        updateGroupNode(orgId, root_id, tree, 0, null, levels, store_level, newstore, old_group_id_title_dict, old_group_id_code_dict, old_group_id_status_dict, title_modify, groupcode_modify, validate_list, delete_list, new ArrayList<>());
        if (delete_list.size() > 0) {
            deleteGroup(delete_list);
        }
        validate_list.forEach(grp_id -> userMessageSendService.publishGroupValidateList(orgId, Collections.singletonList(grp_id)));

        //修改了组织title事件
        if (title_modify.size() > 0) {
            userMessageSendService.publishGroupTitleModifyList(orgId, title_modify);
        }
        groupcode_modify.forEach(v -> {
            if (v.isStorecodeModify()) {
                userMessageSendService.publishGroupCodeModify(v);
            }
        });

        //新增节点
        if (newstore.size() > 0) {
            newstore.forEach(node -> {
                //文件上传时node是ObjectDict类型，手动新增时node是dict类型，而且手动新增没有ancestors字段
                if (CollUtil.isNotEmpty(node.getAncestors())) {
                    //ancestors太深又无用，msgpack 会报错，去掉
                    node.setAncestors(Collections.emptyList());
                }
            });
            userMessageSendService.publishCreateStoreList(orgId, root_id, newstore);
        }
        old_lv_dict.keySet().forEach(lv_id -> userMessageSendService.publishGroupLevelDelete(orgId, lv_id));
    }

    /**
     * 删除节点后，子节点关系上移，如果某节点(A)下的失效的节点全部删除，
     * 则隶属关系退化为全部隶属于一个节点(A)
     *
     * @param groupIdList
     * @return
     */
    private Integer deleteGroup(List<String> groupIdList) {
        groupIdList = groupIdList.stream().map(org.bson.types.ObjectId::new).map(org.bson.types.ObjectId::toHexString).collect(Collectors.toList());
        for (String groupId : groupIdList) {
            UserGroup group = userGroupMapper.selectByPrimaryKey(groupId);
            userGroupMapper.updateParentIdByParentId(group.getGroupId(), group.getGroupParentId());
        }
        userGroupMapper.updateStatusByIds(groupIdList, UserGroupStatusEnum.DELETED.getStatus());
        return groupIdList.size();
    }

    /**
     * @param orgId
     * @param root_id                  根层级ID
     * @param node                     当前组织节点
     * @param lv_idx                   层级下标，第一层级为0，第二层级为1，以此类推
     * @param parentID                 父节点ID
     * @param levels                   层级数据
     * @param store_level
     * @param newstore
     * @param old_group_id_title_dict
     * @param old_group_id_code_dict
     * @param old_group_id_status_dict
     * @param title_modify
     * @param groupcode_modify
     * @param validate_list
     * @param delete_list
     * @return True: 有效子节点, False: 子节点全为隐藏叶节点
     */
    private boolean updateGroupNode(String orgId, String root_id, JSONObject node, int lv_idx, String parentID, List<JSONObject> levels, Integer store_level, List<StoreInfo> newstore, Map<String, String> old_group_id_title_dict, Map<String, String> old_group_id_code_dict, Map<String, Integer> old_group_id_status_dict, List<GroupNameInfo> title_modify, List<ChangeGroupCodeMessage> groupcode_modify, List<String> validate_list, List<String> delete_list, List<String> group_parent_ids) {
        if (StrUtil.isNotEmpty(parentID)) {
            group_parent_ids.add(parentID);
        }
        String lv_id = levels.get(lv_idx).getString("id");
        String grp_id = node.getString("id");
        String admin_id = node.getString("adminID");
        if (StrUtil.isNotEmpty(admin_id)) {
            admin_id = new org.bson.types.ObjectId(admin_id).toHexString();
        }
        UserGroup userGroup = new UserGroup();
        userGroup.setOrgId(orgId);
        Integer status = (Integer) node.getOrDefault("status", GROUP_STATUS_ACTIVATED.getIndex());
        boolean ret = GROUP_STATUS_ACTIVATED.getIndex().equals(status);
        //修改层级名称
        userGroup.setGroupName(node.getString("title"));
        userGroup.setGroupLevelId(new org.bson.types.ObjectId(lv_id).toHexString());
        userGroup.setGroupStatus(status);
        userGroup.setAdminId(admin_id);
        userGroup.setAdminName((String) node.getOrDefault("adminName", ""));
        userGroup.setGroupParentId(parentID);
        boolean new_id;
        String code = node.getString("code");
        if (StrUtil.isEmpty(grp_id)) {
            grp_id = new org.bson.types.ObjectId().toHexString();
            int seq = sequenceService.incrementVersion("group_seq_" + orgId);
            userGroup.setSeq(seq);
            node.put("seq", seq);
            new_id = true;
            //最后一级层级为门店
            if (store_level.equals(node.get("level"))) {
                Map<String, JSONObject> data = new HashMap<>(2);
                data.put(grp_id, node);
                //新的门店
                newstore.add(new StoreInfo(grp_id, code, node.getString("title"), node.getJSONArray("ancestors")));
            }
        } else {
            // 如果grp_id 已存在则是修改组织，判断组织title是否修改
            // 获取old_title
            userGroup.setUpdateTime(new Date());
            String old_title = old_group_id_title_dict.get(grp_id);
            String old_code = old_group_id_code_dict.get(grp_id);
            Integer old_status = old_group_id_status_dict.getOrDefault(grp_id, GROUP_STATUS_ACTIVATED.getIndex());
            Integer level = node.getInteger("level");
            if (StrUtil.isNotEmpty(old_title)) {
                String title = node.getString("title");
                if (StrUtil.isNotEmpty(title) && !old_title.equals(title)) {
                    if (store_level.equals(level)) {
                        //如果确认修改组织title
                        title_modify.add(new GroupNameInfo(grp_id, old_title, title));
                    }
                }
            }
            if (StrUtil.isNotEmpty(old_code)) {
                if (StrUtil.isNotEmpty(code) && !old_code.equals(code)) {
                    //如果确认修改组织code
                    groupcode_modify.add(new ChangeGroupCodeMessage(orgId, grp_id, old_code, code, store_level.equals(level)));
                }
            }
            //如果原来的状态不是GROUP_STATUS_ACTIVATED 那么该节点是其他状态节点恢复为有效节点
            if (!GROUP_STATUS_ACTIVATED.getIndex().equals(old_status) && !GROUP_STATUS_HIDDEN.getIndex().equals(old_status)) {
                validate_list.add(grp_id);
            }
            grp_id = new org.bson.types.ObjectId(grp_id).toHexString();
            new_id = false;
        }
        userGroup.setGroupCode(StrUtil.isNotEmpty(code) ? code : "AUTO_" + sequenceService.incrementVersion("grp_code_" + orgId));
        userGroup.setGroupId(grp_id);
        List<String> member = (List<String>) node.get("member");
        if (CollUtil.isNotEmpty(member)) {
            UserUserGroup group = new UserUserGroup();
            group.setRootGroupId(root_id);
            group.setGroupId(grp_id);
            member.forEach(user_id -> {
                //user_id = new org.bson.types.ObjectId(user_id).toHexString();
                group.setUserId(user_id);
                List<UserUserGroup> userUserGroups = userUserGroupMapper.selectBySelective(group);
                if (userUserGroups.size() == 0) {
                    group.setUserGroupId(new org.bson.types.ObjectId().toHexString());
                    userUserGroupMapper.insert(group);
                }
            });
        }
        //若当前层级下标小于最大的下标则不是最后一个层级，递归调用当前方法
        if (lv_idx < levels.size() - 1) {
            //不是叶子节点即允许存在子节点
            userGroup.setHaveChildrenFlag(HaveChildrenFlagEnum.HAVE.getStatus());
            List<String> children_order = new ArrayList<>();
            List<JSONObject> children = Optional.ofNullable(node.getJSONArray("children")).orElse(new JSONArray()).toJavaList(JSONObject.class);
            for (JSONObject child : children) {
                //拼接父节点
                boolean child_valid = updateGroupNode(orgId, root_id, child, lv_idx + 1, grp_id, levels, store_level, newstore, old_group_id_title_dict, old_group_id_code_dict, old_group_id_status_dict, title_modify, groupcode_modify, validate_list, delete_list, new ArrayList<>(group_parent_ids));
                if (child_valid) {
                    String seq = child.getString("seq");
                    if (StrUtil.isNotEmpty(seq)) {
                        children_order.add(seq);
                    }
                    ret = true;
                }
            }
            userGroup.setChildrenOrder(children_order.toString());
        }
        userGroup.setGroupParentIds(JSONObject.toJSONString(group_parent_ids));
        if (ret) {
            if (Objects.isNull(userGroupMapper.selectByPrimaryKey(grp_id))) {
                userGroupDao.insertSelective(userGroupConvert.mysqlToDo(userGroup));
            } else {
                userGroupMapper.updateByPrimaryKeySelective(userGroup);
            }
        } else if (!new_id) {
            //已经存在的id
            delete_list.add(grp_id);
        }
        return ret;
    }

    /**
     * 获取GroupLevel层级关系
     *
     * @param root_group_id Group.oid
     */
    private List<UserGroupLevel> getGrouplevels(String root_group_id) {
        UserGroup userGroup = userGroupMapper.selectByPrimaryKey(root_group_id);
        if (Objects.isNull(userGroup)) {
            throw new BusinessException(String.valueOf(ResourceErrorCodeConstants.NOT_FOUND), String.format("Group[id=%s] not found", root_group_id));
        }
        if (StrUtil.isNotEmpty(userGroup.getGroupParentId())) {
            //不是根节点
            throw new BusinessException("不是根节点");
        }
        String level_id = userGroup.getGroupLevelId();
        UserGroupLevel root_group_level = userGroupLevelMapper.selectByPrimaryKey(level_id);
        if (Objects.isNull(root_group_level)) {
            //没有找到这个根层级结构，需要从头找一下
            root_group_level = getRootGrouplevel(userGroup.getOrgId());
            if (Objects.isNull(root_group_level)) {
                //还是没有，那就是真没有了
                return Collections.emptyList();
            }
            // 更新保存
            UserGroup param = new UserGroup();
            param.setGroupId(userGroup.getGroupId());
            param.setGroupLevelId(root_group_level.getGroupLevelId());
            userGroupMapper.updateByPrimaryKey(param);
        }
        List<UserGroupLevel> levels = new ArrayList(Arrays.asList(root_group_level));
        level_id = root_group_level.getLevelChildrenId();
        int num = 0;
        while (num < 100) {
            if (StrUtil.isEmpty(level_id)) {
                break;
            }
            UserGroupLevel level = userGroupLevelMapper.selectByPrimaryKey(level_id);
            if (Objects.isNull(level)) {
                log.error("level[id={}] not found", level_id);
                break;
            }
            levels.add(level);
            level_id = level.getLevelChildrenId();
            num++;
        }
        if (num > 99) {
            throw new BusinessException(String.valueOf(FORBIDDEN.getCode()), "group level too deep");
        }
        return levels;
    }

    private UserGroupLevel getRootGrouplevel(String orgId) {
        UserGroupLevel groupLevel = new UserGroupLevel();
        groupLevel.setOrgId(orgId);
        groupLevel.setLevelNum(0);
        return userGroupLevelMapper.selectBySelective(groupLevel).stream().findFirst().orElse(null);
    }

    /*@Override
    public void multiGroupTree(String orgId, Boolean is_total) {
        UserGroup root = getGroupRoot(orgId);
        if (Objects.isNull(root)) {
            throw new BusinessException(String.valueOf(ResourceErrorCodeEnum.NOT_FOUND.getCode()), String.format("Group root node not found"));
        }
        List<UserGroupLevel> level_list = getGrouplevels(root.getGroupLevelId());
        List<String> levels = level_list.stream().map(UserGroupLevel::getGroupLevelId).collect(Collectors.toList());
        TokenDTO token = SecurityContextHolder.getToken();
        if (Objects.equals(UserEnum.USER_NO.getFlag(), token.getIsSenior())
                && Objects.equals(UserEnum.USER_NO.getFlag(), token.getIsSenior()) && !is_total) {
            UserUserGroup userUserGroup = new UserUserGroup;
            userUserGroup.setRootGroupId(root.getGroupId());
            userUserGroup.setUserId(root.getGroupId());
            List<UserUserGroup> userUserGroups = userUserGroupMapper.selectBySelective(userUserGroup);
            if (userUserGroups.isEmpty()) {
                throw new BusinessException(String.valueOf(OUT_OF_GROUP.getCode()), "current user is out of group");
            }

        }
    }


    private UserGroupLevel getRootGrouplevel(String id, boolean no_throw) {
        UserGroupLevel groupLevel = userGroupLevelMapper.selectByPrimaryKey(id);
        if (!no_throw && Objects.isNull(groupLevel)) {
            throw new BusinessException(String.valueOf(ResourceErrorCodeEnum.NOT_FOUND.getCode()), String.format("GroupLevel[id=%s] not found", id));
        }
        return groupLevel;
    }


    private UserGroup getGroupRoot(String orgId) {
        UserOrganization userOrganization = getOrganization(orgId, true);
        if (Objects.nonNull(userOrganization) && StrUtil.isNotEmpty(userOrganization.getRootGroupId())) {
            return getGroup(userOrganization.getRootGroupId(), true);
        }
        return null;
    }

    private UserGroup getGroup(String id, boolean no_throw) {
        UserGroup userGroup = userGroupMapper.selectByPrimaryKey(id);
        if (!no_throw && Objects.isNull(userGroup)) {
            throw new BusinessException(String.valueOf(ResourceErrorCodeEnum.NOT_FOUND.getCode()), String.format("Group[id=%s] not found", id));
        }
        return userGroup;
    }

    private UserOrganization getOrganization(String orgId, boolean no_throw) {
        UserOrganization userOrganization = userOrganizationMapper.findById(orgId);
        if (!no_throw && Objects.isNull(userOrganization)) {
            throw new BusinessException(String.valueOf(ResourceErrorCodeEnum.NOT_FOUND.getCode()), String.format("Organization[id=%s] not found", orgId);
        }
        return userOrganization;
    }*/
}
